#include "State.h"


using namespace std;

//constructor
State::State(void)
{
  noPlayers = 12;
  loadtime = turntime = 0;
  turn = 0;
  turns = 0;
  gameover = 0;
}

//deconstructor
State::~State(void)
{
}


//resets all non-water squares to land and clears the bots ant vector
void State::reset(void)
{
    TRACK();

    myAnts.clear();
    enemyAnts.clear();
    myHills.clear();
    enemyHills.clear();
    food.clear();
    for(uLocation loc=0; loc!=torus.size; ++loc)
      map[loc].reset_all();
}



void 
State::setup(void)
{

}


void 
State::restore(void)
{
  //TRACK();

  // Clear unstable information
  for(Map<Square>::iterator it = map.begin(); it != map.end(); ++it)
    it->reset_unstable();

  // Restore food positions
  for(std::vector<uLocation>::const_iterator cell_it = food.begin(); cell_it != food.end(); ++cell_it)
    if(map[*cell_it].isVisible)
      map[*cell_it].isFood = true;

  // Restore myAnts position
  for(std::vector<uLocation>::const_iterator cell_it = myAnts.begin(); cell_it != myAnts.end(); ++cell_it)
    map[*cell_it].ant = 0;

  // Restore enemyAnts positions
  for(std::vector<Ant>::const_iterator ant_it = enemyAnts.begin(); ant_it != enemyAnts.end(); ++ant_it)
    if(map[ant_it->loc].isVisible)
      map[ant_it->loc].ant = ant_it->team;

  // Restore myHills positions
  for(std::vector<uLocation>::const_iterator cell_it = myHills.begin(); cell_it != myHills.end(); ++cell_it)
    if(map[*cell_it].isVisible)
      {
	map[*cell_it].isHill = true;
	map[*cell_it].hillPlayer = 0;      
      }

  // Restore enemyHills positions
  for(std::vector<Ant>::const_iterator ant_it = enemyHills.begin(); ant_it != enemyHills.end(); ++ant_it)
    if(map[ant_it->loc].isVisible)
      {
	map[ant_it->loc].isHill = true;
	map[ant_it->loc].hillPlayer = ant_it->team;      
      }



}


//outputs move information to the engine
void State::makeMove(const uLocation &loc, unsigned direction)
{
  //    TRACK();
  ASSERT(direction<Torus::TDIRECTIONS);

  cout << "o " << torus.get_row(loc) << " " << torus.get_col(loc)
       << " " << torus.direction_name(direction) << endl;

  uLocation nLoc = torus.getLocation(loc, direction);
  ASSERT(isGround(map.at(nLoc)));
  map.at(nLoc).ant = map.at(loc).ant;
  map.at(loc).ant = -1;
}



void State::updateNeighbourInformation()
{
  TRACK();

  for(std::vector<uLocation>::const_iterator ant_it = myAnts.begin();
      ant_it != myAnts.end(); ++ant_it)
    {
      map[*ant_it].neighbours++;
      map[*ant_it].wasVisited = true;
      for(unsigned d=0; d<Torus::TDIRECTIONS; d++)
	{
	  uLocation nLoc = torus.getLocation(*ant_it, d);
	  if(not map[nLoc].isWater) 
	    {
	      map[nLoc].neighbours++;
	      map[nLoc].wasVisited = true;
	    }
	}
    }
}


/*
    This function will update update the lastSeen value for any squares currently
    visible by one of your live ants.

    BE VERY CAREFUL IF YOU ARE GOING TO TRY AND MAKE THIS FUNCTION MORE EFFICIENT,
    THE OBVIOUS WAY OF TRYING TO IMPROVE IT BREAKS USING THE EUCLIDEAN METRIC, FOR
    A CORRECT MORE EFFICIENT IMPLEMENTATION, TAKE A LOOK AT THE GET_VISION FUNCTION
    IN ANTS.PY ON THE CONTESTS GITHUB PAGE.
*/
void State::updateVisionInformation()
{
  TRACK();


  std::queue<uLocation> locQueue;

  for(std::vector<uLocation>::const_iterator ant_it = myAnts.begin();
      ant_it != myAnts.end(); ++ant_it)
    {
      uLocation sLoc(*ant_it);
      locQueue.push(sLoc);

      visited.reset(); // mandatory to clear the set
      map[sLoc].wasVisible = map[sLoc].isVisible = true;
      visited.insert(sLoc);

      while(!locQueue.empty())
        {
	  uLocation cLoc = locQueue.front();
	  locQueue.pop();

	  for(unsigned d=0; d<Torus::TDIRECTIONS; d++)
            {
	      uLocation nLoc = torus.getLocation(cLoc, d);
	      
	      if(not visited.count(nLoc) && torus.distance(sLoc, nLoc) <= torus.viewRadius)
                {
		  map[nLoc].wasVisible = map[nLoc].isVisible = true;
		  locQueue.push(nLoc);
                }
	      visited.insert(nLoc);
            }
        }
    }
};

/*
    This is the output function for a state. It will add a char map
    representation of the state to the output stream passed to it.

    For example, you might call "cout << state << endl;"
*/
ostream& operator<<(ostream &os, const State &state)
{
  //TRACK();

  os << "     ";
  for(unsigned col=0; col<torus.cols; col++)
    {
      if(col%10!=0)
	os << col %10;
      else
	os << ' ';	
    }
  
  
  os << std::endl;
  os << state.map << std::endl;
  
  return os;
}

//input function
istream& operator>>(istream &is, State &state)
{
  TRACK();

  unsigned row, col;
  signed player;
  string inputType, junk;
  Map<Square> & map(state.map);


  //finds out which turn it is
  while(is >> inputType)
    {
      //bug << "read:" << inputType << std::endl;

      if(inputType == "end")
        {
	  state.gameover = true;
	  break;
        }
      else if(inputType == "turn")
        {
	  is >> state.turn;
	  break;
        }
      else //unknown line
	getline(is, junk);
    }

  if(state.turn == 0)
    {
      unsigned rows,cols;
      double viewRadius, attackRadius, spawnRadius;

      bug << "read game parameters" << std::endl;

      //reads game parameters
      while(is >> inputType)
        {
	  bug << "read:" << inputType << std::endl;
	  
	  if(inputType == "loadtime")
	    is >> state.loadtime;
	  else if(inputType == "turntime")
	    is >> state.turntime;
	  else if(inputType == "rows")
	    is >> rows;
	  else if(inputType == "cols")
	    is >> cols;
	  else if(inputType == "turns")
	    is >> state.turns;
	  else if(inputType == "player_seed")
	    is >> state.seed;
	  else if(inputType == "viewradius2")
            {
	      is >> viewRadius;
	      viewRadius = sqrt(viewRadius);
            }
            else if(inputType == "attackradius2")
            {
	      is >> attackRadius;
	      attackRadius = sqrt(attackRadius);
            }
            else if(inputType == "spawnradius2")
            {
	      is >> spawnRadius;
	      spawnRadius = sqrt(spawnRadius);
            }
            else if(inputType == "ready") //end of parameter input
	      {
                state.timer.start();
		torus.init(rows,cols,attackRadius,viewRadius,spawnRadius);
		depth_map.init();
		map.init();
		state.map_copy.init();
                break;
	      }
            else    //unknown line
	      getline(is, junk);
        }
    }
    else
    {
      bug << "read game state" << std::endl;

      //reads information about the current turn
      while(is >> inputType)
        {
	  //bug << "read:" << inputType << std::endl;

	  if(inputType == "w") //water square
            {
	      is >> row >> col;
	      map.at(row,col).isWater = true;
            }
	  else if(inputType == "f") //food square
            {
	      is >> row >> col;
	      map.at(row,col).isFood = true;
	      state.food.push_back(torus.to_uLocation(row, col));
            }
	  else if(inputType == "a") //live ant square
            {
	      is >> row >> col >> player;
	      ASSERT(player<127);
	      map.at(row,col).ant = player;
	      
	      if(player == 0)
		{
		  state.myAnts.push_back(torus.to_uLocation(row, col));
		}
	      else
		{
		  Ant enemy(torus.to_uLocation(row, col),player);
		  state.enemyAnts.push_back(enemy);
		}
            }
	  else if(inputType == "d") //dead ant square
            {
	      is >> row >> col >> player;
	      ASSERT(player<127);
	      //This was probably the source of all our memory problems:
	      //map.at(row,col).deadAnts.push_back(player);
	      //more economic stuff now:
	      map.at(row,col).wasDead = true;
            }
            else if(inputType == "h")
	      {
		is >> row >> col >> player;
		ASSERT(player<127);
		map.at(row,col).isHill = true;
		map.at(row,col).hillPlayer = player;
		if(player == 0)
		  {
		    state.myHills.push_back(torus.to_uLocation(row, col));
		  }
	      else
                {
		  Ant enemy_hill(torus.to_uLocation(row, col),player);
		  state.enemyHills.push_back(enemy_hill);
                }
	      }
            else if(inputType == "players") //player information
	      {
		is >> state.noPlayers;
		ASSERT(state.noPlayers<127);
	      }
            else if(inputType == "scores") //score information
	      {
		state.scores = vector<double>(state.noPlayers, 0.0);
		for(int p=0; p<state.noPlayers; p++)
		  is >> state.scores[p];
	      }
            else if(inputType == "go") //end of turn input
	      {
		if(state.gameover)
		  is.setstate(std::ios::failbit);
		else
		  state.timer.start();
		break;
	      }
            else //unknown line
	      getline(is, junk);
        }
    }
  
  bug << " I/O done"
      << std::endl;
  
  return is;
}



